//
//  trainingViewController.m
//
//  Created by Eric van Riet Paap on 13-09-08.
//  Copyright __MyCompanyName__ 2008. All rights reserved.
//

#import <QuartzCore/QuartzCore.h>

#import "trainingViewController.h"
#import "MyCLController.h"
#import "TrainingGpsResult.h"


@implementation trainingViewController

@synthesize	myView, background, navbar, content, training_result, training_schedule, tapedeck_left, tapedeck_play, tapedeck_right, distance, maps, training_phase, hold_progress, progress, timer;
@synthesize	hold_progress_done, hold_progress_todo, progress_done, progress_todo;


-(id)init {
	self = [super init];
	self.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Training" image:[UIImage imageNamed:@"training.png"] tag:0]; 
	return self;
}


-(Training*)createTraining {
	NSString		*reference_static_time_preference   = [[NSUserDefaults standardUserDefaults] stringForKey:@"reference_static_time_preference"];
	int				trainingtype_preference = [[NSUserDefaults standardUserDefaults] integerForKey:@"trainingtype_preference"];
	
	NSArray			*x = [reference_static_time_preference componentsSeparatedByCharactersInSet:[NSCharacterSet punctuationCharacterSet]];
	int				ref_static = [[x objectAtIndex:0] intValue] * 60 + [[x objectAtIndex:1] intValue];
	
	NSMutableArray	*holds = [[NSMutableArray arrayWithObjects:nil] retain];
	Training		*tr = nil;
	
	reference_static_time = 100*60; //default: don't ever get to the minor and major ticks
	
	switch (trainingtype_preference) {
		case 0: //drywalk [recovery pyramidical] (10% of p.b.)
			[Hold addArrayWithRepeats:holds initial_recovery: 0 repeat:6 hold:(int)(ref_static*0.10) recovery:12];
			[Hold addArrayWithRepeats:holds initial_recovery:12 repeat:7 hold:(int)(ref_static*0.10) recovery: 8];
			[Hold addArrayWithRepeats:holds initial_recovery: 8 repeat:7 hold:(int)(ref_static*0.10) recovery: 4];
			[Hold addArrayWithRepeats:holds initial_recovery: 4 repeat:7 hold:(int)(ref_static*0.10) recovery: 8];
			[Hold addArrayWithRepeats:holds initial_recovery: 8 repeat:6 hold:(int)(ref_static*0.10) recovery:12];
			tr = [Training trainingWithName:@"Drywalk (pyramidical recovery)" holds:holds];
			break;
			
		case 1: //one breath per hold [pyramidical] (25%..30% of p.b., ~30 minutes)
		{
			int	n = 30*60/(ref_static*0.275);
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:n/5 hold:(int)(ref_static*0.25 ) recovery:0];
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:n/4 hold:(int)(ref_static*0.275) recovery:0];
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:n/4 hold:(int)(ref_static*0.30 ) recovery:0];
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:n/4 hold:(int)(ref_static*0.275) recovery:0];
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:n/5 hold:(int)(ref_static*0.25 ) recovery:0];
			tr = [Training trainingWithName:@"One breath per hold (pyramidical)" holds:holds];
		}
			break;
			
		case 2:	//high CO2 tolerance training [linear] (max. 60% of p.b.) -6.666% per hold
			for (int i = 0;i < 8;i++) {
				float	r = 60 - (60 * 0.0666 * i);
				if (i == 0)	r = 0.0;
				//NSLog([NSString stringWithFormat:@"hold %d, recover %d:%02d", i, (int)r / 60, (int)r % 60]);
				[Hold addArrayWithRepeats:holds initial_recovery:r repeat:1 hold:(int)(ref_static*0.60) recovery:0];				
			}
			tr = [Training trainingWithName:@"High CO2 tolerance" holds:holds];
			break;
			
		case 3: //low O2 tolerance training [linear] (max. 80% of p.b.) +15sec per hold
			for (int i = 0;i < 8;i++) {
				float	hold_time_inc;
				if      (ref_static < 60.0)	hold_time_inc = 3.0;
				else if (ref_static < 90.0)	hold_time_inc = 5.0;
				else						hold_time_inc = 15.0;
				[Hold addArrayWithRepeats:holds initial_recovery:i ? 90.0 : 0.0 repeat:1 hold:(int)(ref_static*0.80-(7-i)*hold_time_inc) recovery:0];				
			}
			tr = [Training trainingWithName:@"Low O2 tolerance" holds:holds];
			break;
			
		case 4: //IHT (intermittent hypoxic training)
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:10 hold:5*60 recovery:4*60];
			tr = [Training trainingWithName:@"IHT" holds:holds];
			break;
			
		case 5: //Max. attempts (for recording statistics)
			[Hold addArrayWithRepeats:holds initial_recovery:0 repeat:1 hold:30*60 recovery:10*60];
			tr = [Training trainingWithName:@"Max. attempts" holds:holds];
			reference_static_time = ref_static;
			break;
			
		default:
			NSAssert(NO, @"Unknown trainingtype (set in preferences)");
			break;
	}
	
	return tr;
}


- (void)loadView {

	//XXX todo: handle case when user did not yet access preferences (values are nil)
	//NSString	*name_preference = [[NSUserDefaults standardUserDefaults] stringForKey:@"name_preference"];

	training = [self createTraining];
	
	//XXX need to put the trainingGpsResults + startTime,etc together in a single class
	trainingGpsResults = [[NSMutableArray arrayWithObjects:nil] retain];
	trainingGpsResultsDistance = 0.0;

	currentLatitude = currentLongitude = currentAccuracy = 0.0;
	currentHold = nil;
	
	startTime = endTime = pauseTime = nil;
	
	int	h = 480;
	int	w = 320;

	
	int	sliderh = 16;
	
	int buttonw = 45;
	int buttonh = 45;
	int	buttonx = w / 2 - buttonw / 2;
	int buttony = h - 114; //XXX we are subtracting the tabbar-height here, don't hardcode!

	[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];

	// create and configure the view
	myView = [[UIView alloc] initWithFrame:CGRectMake(0, 18, w, h)]; //initilize the view    
	myView.autoresizesSubviews = YES;              //allow it to tweak size of elements in view 
	self.view = myView;
	
	background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"TrainingBackground.png"]];
	[self.view addSubview:background]; 
	
	navbar = [UINavigationBar new];
	navbar.barStyle = UIBarStyleBlackTranslucent;
	[navbar sizeToFit];
	//[navbar pushNavigationItem:[[[UINavigationItem alloc] initWithTitle:@"button1"] retain] animated:YES];
	[navbar pushNavigationItem:[[[UINavigationItem alloc] initWithTitle:@"Single Breath"] retain] animated:YES];
	
	/*
	UIButton	*info = [UIButton buttonWithType:UIButtonTypeInfoLight];
	info.frame = CGRectMake(280.0, 0.0, 40.0, 40.0);
	[info addTarget:self action:@selector(infoUpAction:) forControlEvents:UIControlEventTouchUpInside];
	[navbar addSubview:info];
	*/
	
	/*
	UISegmentedControl	*segCtl = [[UISegmentedControl alloc] initWithFrame:CGRectMake(70.0, 8.0, 180.0, 30.0)];
	segCtl.segmentedControlStyle = UISegmentedControlStyleBar;
	[segCtl insertSegmentWithTitle:@"All" atIndex:0 animated:YES];
	[segCtl insertSegmentWithTitle:@"Missed" atIndex:1 animated:YES];
	segCtl.selectedSegmentIndex = 0;
	//[segCtl setDelegate:self];
	[navbar addSubview:segCtl];
	*/
		
	[self.view addSubview:navbar]; 
	
	/*
	UIToolbar *toolbar = [UIToolbar new];
	toolbar.barStyle = UIBarStyleBlackOpaque;
	[toolbar sizeToFit];
	NSMutableArray *items = [[NSMutableArray alloc] init];
	UIBarButtonItem *btn1 = [[[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered       target:nil action:nil] autorelease];
	UIBarButtonItem *btn2 = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease];
	UIBarButtonItem *btn3 = [[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone           target:nil action:nil] autorelease];
	[items addObject:btn1];
	[items addObject:btn2];
	[items addObject:btn3];	
	[toolbar setItems:items animated:YES];
	[items release];
	[self.view addSubview:toolbar]; 
	*/
	
	content = [[UIView alloc] initWithFrame:CGRectMake(0, 44, w, buttony - 2 * buttonh - 14)];
	content.backgroundColor = [UIColor clearColor];
	content.clipsToBounds = YES;
	[self.view addSubview:content];
	
	training_result = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, w, buttony - 2 * buttonh - 14)];
	training_result.backgroundColor = [UIColor clearColor];
	training_result.alpha = 0.7;
	NSString *imagePath = [[NSBundle mainBundle] resourcePath];
	imagePath = [imagePath stringByReplacingOccurrencesOfString:@"/" withString:@"//"];
	imagePath = [imagePath stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
	[training_result loadHTMLString:@"<body bgcolor='black' text='white'><center><b>Page for entering training comments</b><br><img src='Icon.png' /> <img src='tapedeck_play.png' /><br><img src='http://upload.wikimedia.org/wikipedia/en/a/ab/Apple-logo.png' /></center></body>"
					baseURL:[NSURL URLWithString: [NSString stringWithFormat:@"file:/%@//",imagePath]]];
	//[content addSubview:training_result];
	
	training_schedule = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, w, buttony - 2 * buttonh - 14)];
	training_schedule.backgroundColor = [UIColor clearColor];
	training_schedule.alpha = 0.7;
	[training_schedule loadHTMLString:[training asHTML] baseURL:nil];
	[content addSubview:training_schedule];
	
	UIImageView	*dimmer = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"tapedeck_gradient.png"]];
	dimmer.frame = CGRectMake(0, buttony - 3 * sliderh - 12, w, 3 * sliderh + 12);
	dimmer.alpha = 0.85;
	[self.view addSubview:dimmer];	
	
	UIImageView	*gradient = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"tapedeck_gradient.png"]];
	gradient.frame = CGRectMake(0, buttony, w, buttonh);
	//gradient.alpha = 0.5;
	[self.view addSubview:gradient];
	
	tapedeck_left = [[UIButton buttonWithType:UIButtonTypeCustom] retain];    
	tapedeck_left.frame = CGRectMake(buttonx - 2 * buttonw, buttony, buttonw, buttonh);
	[tapedeck_left setBackgroundImage:[UIImage imageNamed:@"tapedeck_left.png"] forState:UIControlStateNormal];
	tapedeck_left.showsTouchWhenHighlighted = YES;
    tapedeck_left.enabled = NO;
	[tapedeck_left addTarget:self action:@selector(leftUpAction:) forControlEvents:UIControlEventTouchUpInside];
	[self.view addSubview:tapedeck_left];
	
	tapedeck_play = [[UIButton buttonWithType:UIButtonTypeCustom] retain];    
	tapedeck_play.frame = CGRectMake(buttonx, buttony, buttonw, buttonh);
	[tapedeck_play setBackgroundImage:[UIImage imageNamed:@"tapedeck_play.png"] forState:UIControlStateNormal];
	tapedeck_play.showsTouchWhenHighlighted = YES;
	[tapedeck_play addTarget:self action:@selector(playPauseUpAction:) forControlEvents:UIControlEventTouchUpInside];
	[self.view addSubview:tapedeck_play];

	tapedeck_right = [[UIButton buttonWithType:UIButtonTypeCustom] retain];    
	tapedeck_right.frame = CGRectMake(buttonx + 2 * buttonw, buttony, buttonw, buttonh);
	[tapedeck_right setBackgroundImage:[UIImage imageNamed:@"tapedeck_right.png"] forState:UIControlStateNormal];
	tapedeck_right.showsTouchWhenHighlighted = YES;
	tapedeck_right.enabled = NO;
	[tapedeck_right addTarget:self action:@selector(rightUpAction:) forControlEvents:UIControlEventTouchUpInside];
	[self.view addSubview:tapedeck_right];
	
	distance = [[UILabel alloc] initWithFrame:CGRectMake(0, buttony - 1 * sliderh - 2, w, 20)];    
	distance.textColor = [UIColor whiteColor];
	distance.backgroundColor = [UIColor clearColor];
	distance.hidden = YES; //un-hide when GPS location is available
	[self updateDistanceText];
	[self.view addSubview:distance];

	maps = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];    
	maps.frame = CGRectMake(buttonx + 3 * buttonw, buttony, buttonw, buttonh);
	maps.showsTouchWhenHighlighted = YES;
	[maps setTitle:@"Map" forState:UIControlStateNormal];
	[maps addTarget:self action:@selector(mapsUpAction:) forControlEvents:UIControlEventTouchUpInside];	
	maps.hidden = YES; //un-hide when GPS location is available
	[self.view addSubview:maps];
	
	training_phase = [[UILabel alloc] initWithFrame:CGRectMake(16, buttony - 1 * sliderh - 2, w - 2 * 16, 14)];
    training_phase.textAlignment = UITextAlignmentCenter;
	training_phase.textColor = [UIColor whiteColor];
	training_phase.backgroundColor = [UIColor clearColor];
	[self updateTrainingPhaseTextWithHold:NO];
	[self.view addSubview:training_phase];
	
	//hold_progress bar
	hold_progress_done = [[UILabel alloc] initWithFrame:CGRectMake(16, buttony - 3 * sliderh - 4, w - 2 * 16, 14)];
	hold_progress_done.font = [UIFont boldSystemFontOfSize:14];
	hold_progress_done.textAlignment = UITextAlignmentLeft;
	hold_progress_done.textColor = [UIColor whiteColor];
	hold_progress_done.backgroundColor = [UIColor clearColor];
	//hold_progress_done.backgroundColor = [UIColor blackColor];
	//hold_progress_done.alpha = 0.5;
	hold_progress_done.text = @"0:00";
	[self.view addSubview:hold_progress_done];
	
	hold_progress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; 
	hold_progress.frame = CGRectMake(52, buttony - 3 * sliderh, w - 2 * 52, 16);	
	[self.view addSubview:hold_progress];
	
	hold_progress_todo = [[UILabel alloc] initWithFrame:CGRectMake(16, buttony - 3 * sliderh - 4, w - 2 * 16, 14)];
	hold_progress_todo.font = [UIFont boldSystemFontOfSize:14];
	hold_progress_todo.textAlignment = UITextAlignmentRight;
	hold_progress_todo.textColor = [UIColor whiteColor];
	hold_progress_todo.backgroundColor = [UIColor clearColor];
	hold_progress_todo.text = @"0:00";
	[self.view addSubview:hold_progress_todo];
	
	//total progress bar
	progress_done = [[UILabel alloc] initWithFrame:CGRectMake(16, buttony - 2 * sliderh - 4, w - 2 * 16, 14)];
	progress_done.font = [UIFont boldSystemFontOfSize:14];
	progress_done.textAlignment = UITextAlignmentLeft;
	progress_done.textColor = [UIColor whiteColor];
	progress_done.backgroundColor = [UIColor clearColor];
	progress_done.text = @"0:00";
	[self.view addSubview:progress_done];
	
	progress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; 
	progress.frame = CGRectMake(52, buttony - 2 * sliderh, w - 2 * 52, 16);	
	[self.view addSubview:progress];
	
	progress_todo = [[UILabel alloc] initWithFrame:CGRectMake(16, buttony - 2 * sliderh - 4, w - 2 * 16, 14)];
	progress_todo.font = [UIFont boldSystemFontOfSize:14];
	progress_todo.textAlignment = UITextAlignmentRight;
	progress_todo.textColor = [UIColor whiteColor];
	progress_todo.backgroundColor = [UIColor clearColor];
	progress_todo.text = @"0:00";
	[self.view addSubview:progress_todo];
	
	int t = [training holdAtTime:0.0].end;
	hold_progress_todo.text = [NSString stringWithFormat:@"%d:%02d", t / 60, t % 60];
	t = [training duration];
	progress_todo.text = [NSString stringWithFormat:@"%d:%02d", t / 60, t % 60];
}


// Called when the view is loading for the first time only
// If you want to do something every time the view appears, use viewWillAppear or viewDidAppear
- (void)viewDidLoad {
	[super viewDidLoad];

	playing = NO;	
	//[self updateProgressViews]; //init the timer strings
	timer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:@selector(updateProgressViews) userInfo:nil repeats:YES];
	
	if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"audio_preference"] isEqualToString:@"YES"]) {
		NSLog(@"Initialize SoundEngine");
		AudioSessionInitialize (NULL,NULL,NULL,NULL); // just set these to NULL if you don't want a callback function
		UInt32 sessionCategory = kAudioSessionCategory_AmbientSound;	//mixes background and my audio
		AudioSessionSetProperty( kAudioSessionProperty_AudioCategory, sizeof( sessionCategory ), &sessionCategory );
		//SoundEngine_Initialize(44100);// (note: optional, calling SoundEngine_LoadEffect auto-initializes)
		NSLog(@"SoundEngine initialized");
	} else {
		NSLog(@"Skip initialize SoundEngine");
	}
	
	/*if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"location_preference"] isEqualToString:@"YES"]) {
		NSLog(@"Initialize Location service");
		[MyCLController sharedInstance].delegate = self;	
		// Check to see if the user has disabled location services all together
		// In that case, we just print a message and disable the "Start" button
		if ( ! [MyCLController sharedInstance].locationManager.locationServicesEnabled ) {
			NSLog(@"User disabled location services");
		}
		else {
			[[MyCLController sharedInstance].locationManager startUpdatingLocation];
			NSLog(@"Location service initialized");
		}
	} else */ {
		NSLog(@"Skip initialize Location service");
	}
}


-(void)setDatabase:(sqlite3*)_database{
	database = _database;
}


-(void)mapsUpAction:(id)sender {
	NSLog(@"maps button was clicked");
	
	if (currentLatitude) {
		NSString *url = [NSString stringWithFormat: @"http://maps.google.com/maps?ll=%f,%f", currentLatitude, currentLongitude];
					 //[latlong stringByAddingPercentEscapesUsingEncoding: 4]];
		[[UIApplication sharedApplication] openURL: [NSURL URLWithString:url]];
	}
}


-(void)flipView:(UIView*)theView betweenView:(UIView*)view1 andView:(UIView*)view2 {	
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationDuration:1.0];
	[UIView setAnimationTransition:([view2 superview] ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:theView cache:YES];
	
	if ([view2 superview]) { 
		[view2 removeFromSuperview];
		[theView addSubview:view1];
	} else {
		[view1 removeFromSuperview];
		[theView addSubview:view2];
	}
	[UIView commitAnimations];
}


-(void)infoUpAction:(id)sender {
	NSLog(@"Info was clicked");
	[self flipView:content betweenView:training_result andView:training_schedule];
}


-(void)playPauseUpAction:(id)sender {
	//NSLog(@"Play/Pause was clicked");
	
	playing = !playing;
	if (playing) {
		tapedeck_left.enabled = YES;
		tapedeck_right.enabled =YES;
		if (pauseTime) { //continue a paused training
			NSLog(@"Continue training");
			NSDate	*oldStartTime = startTime;
			NSDate	*oldEndTime = endTime;
			startTime = [[startTime addTimeInterval:-[pauseTime timeIntervalSinceNow]] retain];
			endTime   = [[  endTime addTimeInterval:-[pauseTime timeIntervalSinceNow]] retain];
			[oldStartTime release];
			[oldEndTime release];
			if (pauseTime) [pauseTime release];
			pauseTime = nil;
		} else { //starting a new training
			NSLog(@"Starting a new training");
			if (startTime) [startTime release]; //XXX we need the 'if' otherwise it app crashes when starting a 2nd training, why???
			if (endTime)   [endTime release];	//XXX we need the 'if' otherwise it app crashes when starting a 2nd training, why???
			startTime = [[NSDate date] retain];
			endTime   = [[NSDate dateWithTimeIntervalSinceNow:[training duration]] retain];
			if (trainingGpsResults) [trainingGpsResults release];
			trainingGpsResults = [[NSMutableArray arrayWithObjects:nil] retain];
			trainingGpsResultsDistance = 0.0;
			[self updateDistanceText];
			[self updateDatabaseWithCurrentTraining];
		}
		[tapedeck_play setBackgroundImage:[UIImage imageNamed:@"tapedeck_pause.png"] forState:UIControlStateNormal];
	} else { //pause this training
		NSLog(@"Pause training");
		tapedeck_left.enabled = NO;
		tapedeck_right.enabled = NO;
		if (pauseTime) [pauseTime release]; //XXX we need the 'if' otherwise it app crashes when starting a 2nd training, why???
		pauseTime = [[NSDate date] retain];
		[tapedeck_play setBackgroundImage:[UIImage imageNamed:@"tapedeck_play.png"] forState:UIControlStateNormal];
		[self updateTrainingPhaseTextWithHold:NO];
	}	
}


-(void)leftUpAction:(id)sender {
	NSLog(@"Left was clicked (go to beginning of this or previous hold/breath phase)");
	
	//add to startTime the time done of this hold.
	float	timeToRewindToPreviousHold = 1.0;
	float	trainingDoneSoFar = fabs([startTime timeIntervalSinceNow]);
	float	phaseEndTime;
	float	phaseStartTime = [training phaseRangeAtTime:trainingDoneSoFar - timeToRewindToPreviousHold phase_end:&phaseEndTime];
	float	t = trainingDoneSoFar - phaseStartTime;
	NSDate	*oldStartTime = startTime;
	startTime = [[startTime addTimeInterval:t] retain];
	[oldStartTime release];
}


-(void)rightUpAction:(id)sender {
	NSLog(@"Right was clicked (go to end of this hold/breath phase)");

	//subtract from startTime the remaining time of this hold.
	float	trainingDoneSoFar = fabs([startTime timeIntervalSinceNow]); 
	float	phaseEndTime;
	float	phaseStartTime = [training phaseRangeAtTime:trainingDoneSoFar phase_end:&phaseEndTime];
	float	t = phaseEndTime - phaseStartTime - (trainingDoneSoFar - phaseStartTime);
	NSDate	*oldStartTime = startTime;
	startTime = [[startTime addTimeInterval:-t] retain];
	[oldStartTime release];;
}


-(void)updateDistanceText {
	float	trainingDoneSoFar = fabs([startTime timeIntervalSinceNow]);
	float	km = trainingGpsResultsDistance / 1000.0;
	float	h  = trainingDoneSoFar / 3600.0;
	distance.text = [NSString stringWithFormat:@"%.0fm (%.1fkm/h)", trainingGpsResultsDistance, h ? km / h : 0.0];
}


-(void)updateTrainingPhaseTextWithHold:(BOOL)holding {
	if (playing) {
		if (holding) {
			training_phase.text = (training.name == @"IHT") ? @"Breath low% O2" : @"Hold";
		} else { //breathing
			training_phase.text = (training.name == @"IHT") ? @"Breath 21% O2" : @"Breath";
		}
	} else { //!playing
		training_phase.text = @"Pause";
	}
}


-(void)updateProgressViews {
	static	BOOL			loaded = NO;
	static	SystemSoundID	recoveryBeginSoundID = 0;
	static	SystemSoundID	holdBeginSoundID = 0;
	static	SystemSoundID	finishedSoundID = 0;
	static	SystemSoundID	minorTickSoundID = 0;
	static	SystemSoundID	majorTickSoundID = 0;
	static	NSString*		last_hold_progress_done_text = @"";
	
	if (!loaded) { //audio not working not in Simulator
		if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"audio_preference"] isEqualToString:@"YES"]) {
			NSLog(@"Loading audio samples");
			NSBundle* bundle=[NSBundle mainBundle];
			SoundEngine_LoadEffect([[ bundle pathForResource:@"recoveryBegin" ofType:@"caf"] UTF8String], &recoveryBeginSoundID);
			SoundEngine_LoadEffect([[ bundle pathForResource:@"holdBegin"     ofType:@"caf"] UTF8String], &holdBeginSoundID);
			SoundEngine_LoadEffect([[ bundle pathForResource:@"finished"      ofType:@"caf"] UTF8String], &finishedSoundID);
			SoundEngine_LoadEffect([[ bundle pathForResource:@"Ping"          ofType:@"caf"] UTF8String], &minorTickSoundID);
			SoundEngine_LoadEffect([[ bundle pathForResource:@"Blow"          ofType:@"caf"] UTF8String], &majorTickSoundID);
			NSLog(@"Audio samples loaded");
		} else { //audio disabled, so don't load samples
			NSLog(@"Skip loading audio samples");
		}
		loaded = YES;
	}

	if (playing) {
		float	trainingDoneSoFar = fabs([startTime timeIntervalSinceNow]);
		float	trainingDuration  = [training duration];
		
		Hold*	newHold = [training holdAtTime:trainingDoneSoFar];
		if (newHold) { //will now be in a breath hold phase
			if (newHold != currentHold) { //was in recovery phase or in another breath hold phase (maybe one breath training without a recovery phase)
				NSLog(@"Hold!");
				[self PlayWithSoundID:holdBeginSoundID];
				[self Vibrate2];
			}
		} else { //will now be in a recovery phase
			if (currentHold) { //was in a breath hold phase
				NSLog(@"Breath!");
				[self PlayWithSoundID:recoveryBeginSoundID];
				[self Vibrate];
			}
		}
		/*if (currentHold != newHold) {
			NSLog([NSString stringWithFormat:@"%08x -> %08x", (int)currentHold, (int)newHold]);
		}*/
		currentHold = newHold;
		
		if (currentLatitude) { //skip until we have a real gps location 
			TrainingGpsResult	*tr = [TrainingGpsResult trainingGpsResultWithTimestamp:trainingDoneSoFar 
																	 latitude:currentLatitude 
											                        longitude:currentLongitude 
																	accuracy:currentAccuracy 
																	distance:trainingGpsResultsDistance
								  ];
			int	n = [trainingGpsResults count];
			if (n == 0) { //always add first
				[trainingGpsResults addObject:tr];
			} else { 
				TrainingGpsResult	*last = [trainingGpsResults objectAtIndex:n - 1];
				if (fabs(tr.timestamp - last.timestamp) >= 1.0 &&                       //Store once per second at most and
				    (tr.latitude != last.latitude || tr.longitude != last.longitude) && //only add when location changes and
					tr.accuracy <= last.accuracy) {                                     //with at least as good accuracy.
					[trainingGpsResults addObject:tr];
					if (last.accuracy <= 50.0) { //update trainingGpsResultsDistance when we have reliable GPS data
						trainingGpsResultsDistance += [
													[[CLLocation alloc] initWithLatitude:last.latitude longitude:last.longitude] getDistanceFrom:
													[[CLLocation alloc] initWithLatitude:tr.latitude longitude:tr.longitude]
												   ];
                        tr.distance = trainingGpsResultsDistance;
						[self updateDistanceText];
					}
				}
			}
		}
		
		if (trainingDoneSoFar < trainingDuration) {
			float	phaseEndTime;
			float	phaseStartTime = [training phaseRangeAtTime:trainingDoneSoFar phase_end:&phaseEndTime];
			hold_progress.progress = (trainingDoneSoFar - phaseStartTime) / (phaseEndTime - phaseStartTime);
			int		i = trainingDoneSoFar - phaseStartTime;
			hold_progress_done.text = [NSString stringWithFormat:@"%d:%02d", i / 60, i % 60];
			int		t = phaseEndTime - phaseStartTime - i;
			hold_progress_todo.text = [NSString stringWithFormat:@"%d:%02d", t / 60, t % 60];
			
			if (last_hold_progress_done_text != hold_progress_done.text) {
				last_hold_progress_done_text = hold_progress_done.text;
				if (i >= reference_static_time - 60) {
					int	ii = i - reference_static_time;
					if ((ii % 15) == 0) {
						if ((ii % 60) == 0) {
							NSLog(@"Major tick");
							[self PlayWithSoundID:majorTickSoundID];
						} else {
							NSLog(@"Minor tick");
							[self PlayWithSoundID:minorTickSoundID];
						}
					}
				}
			}
			
			progress.progress = trainingDoneSoFar / trainingDuration;
			i = trainingDoneSoFar;
			progress_done.text = [NSString stringWithFormat:@"%d:%02d", i / 60, i % 60];
			t = trainingDuration - trainingDoneSoFar;
			progress_todo.text = [NSString stringWithFormat:@"%d:%02d", t / 60, t % 60];

			[self updateTrainingPhaseTextWithHold:newHold != nil];
			
		} else { //training finished! XXX todo: set the progress bar texts to their final value
			hold_progress.progress = 1.0;
			progress.progress = 1.0;
			playing = NO;
			tapedeck_left.enabled = NO;
			tapedeck_right.enabled = NO;
			currentHold = nil;
			[tapedeck_play setBackgroundImage:[UIImage imageNamed:@"tapedeck_play.png"] forState:UIControlStateNormal];
			
			[self PlayWithSoundID:finishedSoundID];
			[self Vibrate3];

			[self updateTrainingPhaseTextWithHold:NO];
			NSLog(@"Training finished");
		}
	} //else !playing
}


-(void)updateDatabaseWithCurrentTraining {
	NSLog(@"updateDatabaseWithCurrentTraining begin");
	
	const char *sql = "INSERT INTO training_result (training_id, timestamp, percent_finished, comments) VALUES (?,?,?,?)"; 	//id INTEGER PRIMARY KEY, training_id INTEGER, timestamp REAL, percent_finished REAL, comments TEXT
	sqlite3_stmt *insertStatement;
	
	if (sqlite3_prepare_v2(database, sql, -1, &insertStatement, NULL) == SQLITE_OK) 
	{
		sqlite3_bind_int(insertStatement, 1, 8);		//training_id
		sqlite3_bind_double(insertStatement, 2, 7.5);	//timestamp
		sqlite3_bind_double(insertStatement, 3, 6.5);	//percent_finished
		sqlite3_bind_text(insertStatement, 4, [@"some comment goes here" UTF8String], -1, SQLITE_TRANSIENT);	//comments
		sqlite3_step(insertStatement);
	} else {
		NSLog(@"updateDatabaseWithCurrentTraining insertStatement ERROR");
	}
	
	// "Finalize" the statement - releases the resources associated with the statement.
	sqlite3_finalize(insertStatement);
	NSLog(@"updateDatabaseWithCurrentTraining end");
}


-(void)PlayWithSoundID:(SystemSoundID)soundID {
	if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"audio_preference"] isEqualToString:@"YES"]) {
		SoundEngine_StartEffect(soundID);
		NSLog(@"Play audio sample");
	} else {
		NSLog(@"Skip playing audio sample");
	}
}


-(void)Vibrate {
	if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"vibrate_preference"] isEqualToString:@"YES"]) {
		AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
		NSLog(@"Vibrate");
	} else {
		NSLog(@"Skip Vibrate");
	}
}


-(void)Vibrate2 {
	[self Vibrate];
	[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(Vibrate) userInfo:nil repeats:NO]; //XXX memleak
}


-(void)Vibrate3 {
	[self Vibrate];
	[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(Vibrate2) userInfo:nil repeats:NO]; //XXX memleak
}


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
	// Return YES for supported orientations
	return (interfaceOrientation == UIInterfaceOrientationPortrait);
}


- (void)didReceiveMemoryWarning {
	[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
	// Release anything that's not essential, such as cached data
}


- (void)dealloc {
	[super dealloc];
}



#pragma mark ---- delegate methods for the MyCLController class ----

-(void)newLocationUpdateLog:(NSString *)text {
	//[self addTextToLog:text]; //outcomment this to see timestamps, gps locations, and distances.
}

-(void)newLocationUpdate:(float)latitude longitude:(float)longitude accuracy:(float)accuracy {
	currentLatitude  = latitude;
	currentLongitude = longitude;
	currentAccuracy  = accuracy;
	//maps.hidden = NO;
	if (distance.hidden && trainingGpsResultsDistance > 50.0) {
	    distance.hidden = NO; //only show when we moved a little
    }
}


@end
